/*
模版编译
*/
class Compile{
    constructor(el,vm){
        this.el=this.isElementNode(el)?el:document.querySelector(el)
        this.vm=vm
        if(this.el){
            // 把元素放入文档碎片中
            let fragment=this.node2fragment(this.el)
            // 编译带有v-model {{}}的元素节点
            this.compile(fragment)
            // 把编译好的文档放入页面中（操作文档碎片不会造成页面的重绘和重排）
            this.el.appendChild(fragment)
        }
    }
    // 辅助方法
    isElementNode(node){
        return node.nodeType===1
    }
    isDirective(attr){
        return attr.includes('v-')
    }
    // 主要方法
    compileElement(node){
        let attrs=node.attributes
        Array.from(attrs).some(attr=>{
            if(this.isDirective(attr.name)){ // 判断是不是我们需要的带v-的指令
                // 去data上取到对应的数据放到当前节点上需要（attr.value,this.vm.$data,node），取数据放入html的这个过程单独定义一个工具方法来实现
                let expr=attr.value
                let [,type]=attr.name.split('-')
                CompileUtil[type](node,this.vm,expr)
            }
        })
    }
    compileText(node){
        let text=node.textContent
        let reg=/\{\{([^}]+)\}\}/g
        if(reg.test(text)){ // 判断是不是带有{{}}的文本
        // 需要（expr,this.vm.$data,node）
        let expr=text
        CompileUtil['text'](node,this.vm,expr)        
        }
    }
    compile(fragment){
        let childNodes=fragment.childNodes
        Array.from(childNodes).some(node=>{
            if(this.isElementNode(node)){
                this.compileElement(node)
                this.compile(node) // 如果是元素节点可能还有子节点所以需要递归
            }else{
                this.compileText(node)
            }
        })
    }
    node2fragment(el){
        let fragment=document.createDocumentFragment()
        let firstChild
        while (firstChild=el.firstChild) {
            fragment.appendChild(firstChild)
        }
        return fragment
    }
}
CompileUtil={
    getVal(vm,expr){
        let exprs = expr.split('.')
        return exprs.reduce((prve,next)=>{
            return prve[next]
        },vm.$data) // 收敛方法第三个参数是指定第一次循环的prve的值
    },
    getTextVal(vm,expr){
      return expr.replace(/\{\{([^}]+)\}\}/g,(...arguments)=>{
           return this.getVal(vm,arguments[1]) // 箭头函数中没有arguments想要在箭头函数中使用必须使用剩余运算符解开当作形参传入，replace方法的第一个参数是符合字符串规则的正则表达式，
           // 第二个参数是一个函数时，会去循环找到每一个符合规则的字符，并且存在于形参列表arguments中（这里的replace调用最后得到的结果是每一次循环得到的值把匹配规则去掉后拼接出来的新的字符串）
       })
    },
    text(node,vm,expr){ //编译文本的
        let update=this.updater['textUpdater']
        let value = this.getTextVal(vm,expr)
        expr.replace(/\{\{([^}]+)\}\}/g,(...arguments)=>{
            new Wather(vm,arguments[1],(newValue)=>{
                update && update(node,this.getTextVal(vm,expr)) 
            })
        })
       
        update && update(node,value) // 把每个数据放入html的操作
    },
    setValue(vm,expr,value){
        expr=expr.split('.')
        expr.reduce((prve,next,currentIndex)=>{
            if(currentIndex===expr.length-1){
                return prve[next]=value
            }
            return prve[next]
        },vm.$data)
    },
    model(node,vm,expr){ // 编译表单元素的
        let update=this.updater['modelUpdater']
        let value = this.getVal(vm,expr)
        new Wather(vm,expr,(newValue)=>{
            update && update(node,this.getVal(vm,expr)) 
        })
        node.addEventListener('input',(e)=>{
            let newValue=e.target.value
            this.setValue(vm,expr,newValue)
        })
        update && update(node,value)
    },
    updater:{ // 把方法进行解耦，实现功能职能单一
        textUpdater(node,value){
            node.textContent=value
        },
        modelUpdater(node,value){
            node.value=value
        }
    }
}